# -*- coding: utf-8 -*-
__author__ = 'Jinkey'

"""
循序渐进用 Python 编写你的第一个 Keras 神经网络

# 教程概述

这里不需要编写太多的代码,不过我们将一步步慢慢地告诉你怎么以后怎么创建自己的模型。
教程将会涵盖以下步骤:
1. 加载数据
2. 定义模型
3. 编译模型
4. 训练模型
5. 评估模型
6. 结合所有步骤在一起

这个教程的前置条件:
1. 有 python 2 或 3 的环境和编程基础
2. 安装并配置好 Scipy 库（包括 Numpy ）
3. 你安装好 Keras 并且有一个后端（[Theano](http://deeplearning.net/software/theano/) or [TensorFlow](https://www.tensorflow.org/)）

创建一个新的文件,命名为 keras_first_network.py ,然后将教程的代码一步步复制进去。

# 1. 加载数据
每当我们使用机器学习算法使用随机过程(如随机数),先设置随机数种子是一个好的习惯。

这样您就可以运行相同的代码一次又一次,得到相同的结果。如果你需要证明结果,使用随机数据比较算法或调试代码,这是很有用的。

你可以初始化随机数发生器及其种子,例如:
"""


from keras.models import Sequential
from keras.layers import Dense
import numpy
# fix random seed for reproducibility
seed = 7
numpy.random.seed(seed)


"""
现在我们可以加载我们的数据了。
在这个教程中,我们将使用[皮马印第安人糖尿病数据集](http://archive.ics.uci.edu/ml/datasets/Pima+Indians+Diabetes).这是UCI 机器学习数据库一个标准的机器学习数据集。它描述了病人医疗记录和他们是否在五年内发病。

因此,它是一个二分类问题(出现糖尿病为1, 否则为 0)。
所有描述病人的输入变量都是数值。这便于直接用于需要数值输入输出的神经网络, 适合我们第一个 Keras 神经网络。
下载数据集并且重命名为 'pima-indians-diabetes.csv' 放入 python 脚本所在的目录的 data/pima-indians-diabetes.csv。

你可以只直接使用 Numpy 库的 loadtxt() 方法加载数据,一共 8 个输出变量和 1 个输出变量（最后一列）。加载之后我们就可以把数据分离为 X（输出变量）和 Y（输出分类）

"""

# load pima indians dataset
dataset = numpy.loadtxt("data/pima-indians-diabetes.csv", delimiter=",")
# split into input (X) and output (Y) variables
X = dataset[:, 0:8]
Y = dataset[:, 8]

"""
我们已经初始化了我们对的随机数生成器来确保结果可复现, 也加载了数据。我们现在可以准备定义我们的神经网络模型了。

# 2. 定义模型
Keras 中的模型被定义为一系列的层。

我们实例化一个 Sequential 模型对象,每次添加一层知道我们对网络的拓扑结构满意。

第一件事情我们需要确保的是输出神经元的数量。这可以在模型创建的时候设置参数 input_dim 来定义,我们将这个参数设置为 8 对应 8 个输出变量。
我们怎么知道层的数量和他们的类型呢?

这是一个非常难回答的问题。这是启发式的,我们通过不断地试错找出最好的网络结构、一般来说,你需要足够大的网络去明白结构对于问题是否有用。
在这个例子中, 我们使用三层全连接的结构。

全连接层使用 Dense 定义。我们可以通过第一个参数定义层的神经元数量,第二个参数 init 定义权重的初始化方法, activation 参数定义激活函数。

在这个例子中, 我们把权重初始化成一个服从均匀分布的小随机数（init='uniform'）,在0到0.05直接（这是 Keras 标准均匀分布权重初始值）。另一种传统的选择是‘normal’,会从高斯分布（正态分布）中产生一个小的随机数。

我们在前两层使用 (relu)[https://en.wikipedia.org/wiki/Rectifier_(neural_networks)] 激活函数, 在输出层使用 Sigmoid 函数。曾经 Sigmoid 和 tanh 激活函数是所有的层首选的。但时至今日, 使用 relu 激活函数可以达到更好的性能。我们在输出层使用 Sigmoid 函数来确保网络输出在 0 和 1 之间,

我们可以添加每一层将这些东西放到一起。第一层有 12 个神经元、8个输出变量。第二层有 8 个神经元和最后 1 个神经元的输出层用于预测类别（糖尿病有无发病）

"""

# create model
model = Sequential()
model.add(Dense(12, input_dim=8, init='uniform', activation='relu'))
model.add(Dense(8, init='uniform', activation='relu'))
model.add(Dense(1, init='uniform', activation='sigmoid'))

"""
# 3. 编译模型
现在我们定义好模型, 那么就可以编译他了。

编译使用高效的数学库, 封装了 Theano 或者 TensorFlow（称为 backend）。后端（backend）在你的硬件上自动选择最好的方式去表现用于训练和预测的神经网络,比如 CPU、GPU 或者分布式。

编译时, 我们需要额外定义训练网络所需要的参数。记住, 训练网络意味着寻找最优的权重集去预测。

我们需要定义评估权重集的损失函数, 用于寻找不同权重的优化器以及我们希望在训练过程呈现的可选指标。

在这个例子中, 我们使用对数损失函数（logarithmic loss）, 对于二分类问题, 其在 Keras 中称为“binary_crossentropy”。我们还将使用梯度下降算法‘adam’, 没有为什么, 它就是一种高效地默认方法。想了解更多这种算法可以查看论文: (Adam: A Method for Stochastic Optimization)[http://arxiv.org/abs/1412.6980]

最后, 以为这是一个分类问题, 所以我们会收集和汇报分类的准确率作为度量指标。
"""

# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

"""
# 4. 训练模型
我们已经定义和编译了模型, 他是为高效地计算而准备的。

现在是时候在数据上训练模型了。

我们可以在加载的数据上训练和拟合模型,通过 fit() 函数。

训练过程会在数据集迭代一定的次数,成为 epochs, 这个可以通过 `nb_epoch` 参数来设定。我们也可以设定 `batch_size` 参数来指定进行梯度下降时每个batch包含的样本数。训练时一个batch的样本会被计算一次梯度下降, 使目标函数优化一步。在这个例子中, 我们将迭代150次、批处理大小为10。再说一次, 这些参数可以通过试错来选择。


"""

# Fit the model
model.fit(X, Y, nb_epoch=150, batch_size=10)

"""
这就是在你的 CPU 或者 GPU 上发生的事情。

# 5. 评估模型
我们已经在整个数据集上训练了我们的神经网络, 我们可以在线通的数据集上评估神经网络的性能。

这只会告诉我们模型有多适合已有的数据（训练的准确率）,但我们无从知道算法在新数据上的性能。

我们可以简单但很理想地把数据分为训练集和测试集来分别训练和评估模型。

你可以通过 `evaluate()` 函数在训练集评估你的模型, 使用你训练模型时相同的输出和输出。

这会针对每一个输出-输出产生预测并且收集分数,包括平均损失和其他我们定义的指标,比如准确率。

"""

# evaluate the model
scores = model.evaluate(X, Y)
print("%s: %.2f%%" % (model.metrics_names[1], scores[1]*100))

"""
# 6. 将这些放在一起

你已经看到用 Keras 创建你的第一个神经网络有多么简单、

运行以上的代码, 将会看到150个迭代中, 每次迭代的损失和准确率,以及最终的模型在训练集上的评估结果, 在我的 CPU 上耗时 10s（使用 Theano 作为后端）

> ...
Epoch 143/150
768/768 [==============================] - 0s - loss: 0.4614 - acc: 0.7878
Epoch 144/150
768/768 [==============================] - 0s - loss: 0.4508 - acc: 0.7969
Epoch 145/150
768/768 [==============================] - 0s - loss: 0.4580 - acc: 0.7747
Epoch 146/150
768/768 [==============================] - 0s - loss: 0.4627 - acc: 0.7812
Epoch 147/150
768/768 [==============================] - 0s - loss: 0.4531 - acc: 0.7943
Epoch 148/150
768/768 [==============================] - 0s - loss: 0.4656 - acc: 0.7734
Epoch 149/150
768/768 [==============================] - 0s - loss: 0.4566 - acc: 0.7839
Epoch 150/150
768/768 [==============================] - 0s - loss: 0.4593 - acc: 0.7839
768/768 [==============================] - 0s
acc: 79.56%

如果你尝试在 IPython 或者 Jupyter , 你将会得到错误。原因是在训练期间输出进度条。你可以关闭这个, 通过让 `model.fit()` 的参数 `verbose=0`

# 福利: 做出预测
我被问得最多的一个问题是:

> 在我训练模型之后, 怎么预测新数据的分类?

这是个好问题。

我们拟合了上述例子, 用他来在训练集上作出预测, 假装我们之前没看到过这些数据。

做预测同样非常简单, 只需要使用 `model.predict()`。我们在输出层使用 Sigmoid 激活函数, 因此我们的预测值将会在 0 到 1 的区间内。在这个分类任务中,我们可以轻易地通过四舍五入转换为离散二分类。

预测训练集中每一个记录的完整例子如下:

"""

# Create first network with Keras
from keras.models import Sequential
from keras.layers import Dense
import numpy
# fix random seed for reproducibility
seed = 7
numpy.random.seed(seed)
# load pima indians dataset
dataset = numpy.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# split into input (X) and output (Y) variables
X = dataset[:,0:8]
Y = dataset[:,8]
# create model
model = Sequential()
model.add(Dense(12, input_dim=8, init='uniform', activation='relu'))
model.add(Dense(8, init='uniform', activation='relu'))
model.add(Dense(1, init='uniform', activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# Fit the model
model.fit(X, Y, nb_epoch=150, batch_size=10,  verbose=2)
# calculate predictions
predictions = model.predict(X)
# round predictions
rounded = [round(x) for x in predictions]
print(rounded)

"""
运行这个修改过的例子, 将会打印出每个输出的预测值。如果有需要的话, 我们可以直接使用这些预测。

> [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0]


# 总结
在这篇文章当中, 我们学会了如何通过 Keras 创建自己的第一个神经网络模型。

特别是我们学会了 使用 Keras 来创建神经网络或深度学习模型时关键的 5 个步骤:
1. 加载数据
2. 定义模型
3. 编译模型
4. 训练模型
5. 评估模型

有任何疑问欢迎关注我的公众号 jinkey-love 和我交流。

"""